﻿
namespace System.Extensions.IO
{
  using System;
  using System.Collections.Generic;
  using System.Text;

  /// <summary>
  /// Represents a section in the <see cref="IniFile"/>.
  /// </summary>
  public sealed class IniFileSection : IDirty
  {
    private bool dirty;
    private string name;
    private Dictionary<string, string> entries;

    /// <summary>
    /// Initializes a new instance of the <see cref="IniFileSection"/> class.
    /// </summary>
    /// <param name="name"></param>
    public IniFileSection(string name)
    {
      if (name == null)
      {
        throw new ArgumentNullException("name");
      }

      this.name = name;
      this.entries = new Dictionary<string, string>();
    }

    /// <summary>
    /// Gets or sets the name of this <see cref="IniFileSection"/>.
    /// </summary>
    public string Name
    {
      get
      {
        return this.name;
      }

      set
      {
        if (value == null)
        {
          throw new ArgumentNullException("value");
        }

        if (value.Equals(this.name))
        {
          return;
        }

        this.name = value;
        this.SetDirtyState(true);
      }
    }

    /// <summary>
    /// Gets a value indicating whether this <see cref="IniFileSection"/> is dirty.
    /// </summary>
    public bool IsDirty
    {
      get { return this.dirty; }
      internal set { this.dirty = value; }
    }

    private void SetDirtyState(bool state)
    {
      this.dirty = state;
    }

    /// <summary>
    /// Gets the value for a specified entry.
    /// </summary>
    public string this[string key]
    {
      get
      {
        if (key == null)
        {
          throw new ArgumentNullException("key");
        }

        if (this.entries == null)
        {
          return null;
        }

        if (this.entries.ContainsKey(key))
        {
          return this.entries[key];
        }
        else
        {
          return null;
        }
      }
    }

    /// <summary>
    /// Gets whether an entry with the specified key exists in this <see cref="IniFileSection"/>.
    /// </summary>
    public bool Exists(string key)
    {
      if (this.entries == null)
      {
        throw new InvalidOperationException();
      }

      return this.entries.ContainsKey(key);
    }

    /// <summary>
    /// Adds an entry to this <see cref="IniFileSection"/>.
    /// </summary>
    public void Add(string key, string value)
    {
      if (key == null)
      {
        throw new ArgumentNullException("key");
      }

      if (value == null)
      {
        throw new ArgumentNullException("value");
      }

      if (this.entries == null)
      {
        this.entries = new Dictionary<string, string>(1);
      }

      if (this.entries.ContainsKey(key))
      {
        throw new InvalidOperationException("The key already exists.");
      }
      else
      {
        lock (this.entries)
        {
          this.entries.Add(key, value);
          this.SetDirtyState(true);
        }
      }
    }

    /// <summary>
    /// Removes an entry from the section.
    /// </summary>
    public void Remove(string key)
    {
      if (key == null)
      {
        throw new ArgumentNullException("key");
      }

      if (this.entries == null)
      {
        return;
      }

      if (this.entries.ContainsKey(key))
      {
        lock (this.entries)
        {
          this.entries.Remove(key);
          this.SetDirtyState(true);
        }
      }
    }

    /// <summary>
    /// Sets the value of an entry.
    /// </summary>
    public void Set(string key, string value)
    {
      if (key == null)
      {
        throw new ArgumentNullException("key");
      }

      if (value == null)
      {
        throw new ArgumentNullException("value");
      }

      if (this.entries == null)
      {
        this.entries = new Dictionary<string, string>(1);
      }

      if (this.entries.ContainsKey(key))
      {
        lock (this.entries)
        {
          if (this.entries[key].Equals(value))
          {
            return;
          }

          this.entries[key] = value;
          this.SetDirtyState(true);
        }
      }
      else
      {
        this.Add(key, value);
      }
    }

    /// <summary>
    /// Gets the string represenation of the this <see cref="IniFileSection"/>.
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
      StringBuilder sb = new StringBuilder();
      sb.AppendFormat(
          System.Globalization.CultureInfo.InvariantCulture,
          "[{0}]\r\n",
          this.name);

      if (this.entries != null)
      {
        using (Dictionary<string, string>.Enumerator enu = this.entries.GetEnumerator())
        {
          while (enu.MoveNext())
          {
            sb.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}={1}\r\n", enu.Current.Key, enu.Current.Value);
          }
        }
      }

      return sb.ToString();
    }

    /// <summary>
    /// Gets an enumerator to traverse the entries in this <see cref="IniFileSection"/>.
    /// </summary>
    /// <returns></returns>
    public Dictionary<string, string>.Enumerator GetEnumerator()
    {
      if (this.entries != null)
      {
        return this.entries.GetEnumerator();
      }
      else
      {
        this.entries = new Dictionary<string, string>(0);
        return this.entries.GetEnumerator();
      }
    }
  }
}
